package com.cff.secure;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

/**
 * RSA算法对数据进行加解密工具类, RSA密钥长度为1024。
 *
 * @author ljhuang
 * @version 2015-12-29
 */
public class RSAUtil {

	// 密钥长度1024对应的加解密最大长度
	public static final int MAX_ENCRYPT_BLOCK = 117;
	public static final int MAX_DECRYPT_BLOCK = 128;

	public static final String CHARSET_UTF8 = "UTF-8";
	public static final String KEY_ALGORITHM = "RSA";
	private static final String PUBLIC_KEY = "RSAPublicKey";
	private static final String PRIVATE_KEY = "RSAPrivateKey";

	/**
	 * 公钥加密
	 * @param data
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static byte[] encrypt(String data, PublicKey key) throws Exception {
		return encrypt(data.getBytes(CHARSET_UTF8), key);
	}

	/**
	 * 公钥加密
	 * @param obj
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static byte[] encrypt(Object obj, PublicKey key) throws Exception {
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		ObjectOutputStream oos = new ObjectOutputStream(bos);
		oos.writeObject(obj);
		byte[] b = bos.toByteArray();
		bos.close();
		oos.close();
		return encrypt(b, key);
	}

	/**
	 * 私钥解密
	 * @param data
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static String decryptString(byte[] data, PrivateKey key) throws Exception {
		byte[] buf = decrypt(data, key);
		return new String(buf, CHARSET_UTF8);
	}

	/**
	 * 私钥解密
	 * @param data
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static Object decryptObject(byte[] data, PrivateKey key) throws Exception {
		byte[] buf = decrypt(data, key);
		ByteArrayInputStream bis = new ByteArrayInputStream(buf);
		ObjectInputStream ois = new ObjectInputStream(bis);
		Object obj = ois.readObject();
		bis.close();
		ois.close();
		return obj;
	}

	/**
	 * 公钥加密
	 * @param data
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static byte[] encrypt(byte[] data, PublicKey key) throws Exception {
		Cipher cipher = Cipher.getInstance("RSA");
		cipher.init(Cipher.ENCRYPT_MODE, key);

		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int inputLen = data.length;
		int offset = 0;
		byte[] cache;
		int i = 0;

		while (inputLen - offset > 0) {
			if (inputLen - offset > MAX_ENCRYPT_BLOCK) {
				cache = cipher.doFinal(data, offset, MAX_ENCRYPT_BLOCK);
			} else {
				cache = cipher.doFinal(data, offset, inputLen - offset);
			}
			out.write(cache);
			i++;
			offset = i * MAX_ENCRYPT_BLOCK;
		}

		byte[] decryptedData = out.toByteArray();
		out.close();

		return decryptedData;
	}

	/**
	 * 私钥解密
	 * @param data
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static byte[] decrypt(byte[] data, PrivateKey key) throws Exception {
		Cipher cipher = Cipher.getInstance("RSA");
		cipher.init(Cipher.DECRYPT_MODE, key);

		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int inputLen = data.length;
		int offset = 0;
		byte[] cache;
		int i = 0;

		while (inputLen - offset > 0) {
			if (inputLen - offset > MAX_DECRYPT_BLOCK) {
				cache = cipher.doFinal(data, offset, MAX_DECRYPT_BLOCK);
			} else {
				cache = cipher.doFinal(data, offset, inputLen - offset);
			}
			out.write(cache);
			i++;
			offset = i * MAX_DECRYPT_BLOCK;
		}

		byte[] encryptedData = out.toByteArray();
		out.close();

		return encryptedData;
	}
	
	/**
	 * BASE64解密
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static byte[] decryptBASE64(String key) throws Exception {
		return (new BASE64Decoder()).decodeBuffer(key);
	}

	/**
	 * BASE64加密
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static String encryptBASE64(byte[] key) throws Exception {
		return (new BASE64Encoder()).encodeBuffer(key);
	}
	
	/**
	 * 产生公钥和私钥
	 * @return
	 * @throws Exception
	 */
	public static Map<String, String> initKey() throws Exception {
		KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
		keyPairGen.initialize(1024);
		KeyPair keyPair = keyPairGen.generateKeyPair();
		RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
		RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
		Map<String, String> keyMap = new HashMap<String, String>(2);
		String publicKeyStr = encryptBASE64(publicKey.getEncoded());
		String privateKeyStr = encryptBASE64(privateKey.getEncoded());
		keyMap.put(PUBLIC_KEY, publicKeyStr);
		keyMap.put(PRIVATE_KEY, privateKeyStr);
		return keyMap;

	}
	
	/**
	 * 根据公钥字符串生成RSAPublicKey
	 * @param publicKey
	 * @return
	 * @throws Exception
	 */
	public static RSAPublicKey getPublicKey(String publicKey) throws Exception{ 
		BASE64Decoder base64Decoderpri= new BASE64Decoder(); 
	    byte[] keyBytes = base64Decoderpri.decodeBuffer(publicKey);    
	    X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);    
	    KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);    
	    return (RSAPublicKey) keyFactory.generatePublic(spec);    
	}    
	    
	/**
	 * 根据私钥字符串生成RSAPrivateKey
	 * @param privateKey
	 * @return
	 * @throws Exception
	 */
	public static RSAPrivateKey getPrivateKey(String privateKey) throws Exception{ 
		BASE64Decoder base64Decoderpri= new BASE64Decoder();  
	    byte[] keyBytes = base64Decoderpri.decodeBuffer(privateKey);    
	    PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);    
	    KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);    
	    return (RSAPrivateKey) keyFactory.generatePrivate(spec);    
	}

	
	public static void main(String args[]) throws Exception{
		String pubkey="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrY94hCzbPVMBrjz0/pS1w/dtDmWkoie7nJO5r"
				+ "adP6B8u3zQJNdlrLswydyxV59ppZ3v/sAmLVQ8A0OKHV1pKzCOM45l0gwiXoDcVe/kidHve38X/h"
				+ "zQGUzGRdk3CEGkdkAVzzHI3jrMMj+BJ0XrOCx7TKQTaH1XBjUFYIfAn8xQIDAQAB";
		String pubkey2="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCiONNt9jbxxrZu36R3/rNIKoVYSvqTMSLyqjL5"
				+ "8noKYl6evAvI7BFLqkujE+1B6V5XduFVYWUSKJRdkqdtKq0w8wXPK6KIndST6+VHMpv0qQFQMAMI"
				+ "CpygqsQAsxbU5W/4CRosomeDMrh+ftQOkerbXDE7bJ3itoT59JY8I5KnSwIDAQAB";
		
		String priKey="MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKtj3iELNs9UwGuPPT+lLXD920OZ"
				+ "aSiJ7uck7mtp0/oHy7fNAk12WsuzDJ3LFXn2mlne/+wCYtVDwDQ4odXWkrMI4zjmXSDCJegNxV7+"
				+ "SJ0e97fxf+HNAZTMZF2TcIQaR2QBXPMcjeOswyP4EnRes4LHtMpBNofVcGNQVgh8CfzFAgMBAAEC"
				+ "gYEAl8S9xOEbTgmswxJ6mEt6q94ANiI5Xq6WIZUwk2mJTpVsrnwaF+JAkLUi7PATOodHM2InmiSa"
				+ "xGNUZmnn1WZHTFK52MCeZ2aROBEyaUDEFMKqIAwoMrwm0QglxMkHd5KkY2cqfOyrHVimazNm2sBk"
				+ "AlGQh/ChCtJQTplZOtM03lkCQQDj+sNKW3Fn/2FjLINwqgnIiKKrqmfSNQvwDlKloVRvhC0qEWD9"
				+ "pjf9MIpcl+Lj/PEfStBKnJ3QIThC4XyIK9VbAkEAwHSNhmCzyRkzjrLR7DmhrfmOe5H0YVyL5nFs"
				+ "YLhcJjA0xQrHUJV0WkmT2x1XVYp9M1h5lU2llExHetqcyKpwXwJABHWpUFENnN0wCIKcZ53L5zJV"
				+ "DEqfEUucWivNjhPj1PKe942zegLAYaGLc1g9RfxyswwBmxQJOkMMGLLUfs1stQJAWtco7iK6+dLx"
				+ "aQuy1oOlcw72WBFefo5GYrFq7IRYypW6cr9RsRG6Z9uO9L/2tyO1UQUT7otF+qe+nZiOpJD+lQJA"
				+ "I94knVZli28aMPhGnZcMHRHUcD2wKGA70tYAorBzxqRifMG7mecl2tKYziPYRl20ZqNWaR3ojj48"
				+ "/jlIYGc1qg==";

        RSAPublicKey publicKey= getPublicKey(pubkey); 
		byte []result = encrypt("你是个2b",publicKey);
		String resultStr = encryptBASE64(result);
		System.out.println("resultStr:\n"+resultStr);
		
		 
        RSAPrivateKey privateKey= getPrivateKey(priKey);
		String resultpri = decryptString(decryptBASE64(resultStr),privateKey);
		System.out.println("resultpri:\n"+resultpri);
	}
}
